﻿using System;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using SlamCms.Common;
using SlamCms.Data;
using SlamCms.Logging;
using SlamCms.Profiling;
using SlamCms.Services;

namespace SlamCms.Web
{
	public class SlamCmsContext
	{
		private readonly HttpContextBase _httpContext;
		private readonly ICookieManager _cookieManager;
		private readonly SlamCmsConfiguration _configuration;
		private readonly DataContext _dataContext;
		private readonly AdministrationBar _administrationBar;
		private readonly Lazy<UrlHelper> _urlHelper;
		private bool _showAdminBar;

		public SlamCmsContext(HttpContextBase httpContext, DataContext dataContext, SlamCmsConfiguration configuration)
		{
			_httpContext = httpContext;
			_cookieManager = httpContext.GetCookieManager();
			_configuration = configuration;
			_dataContext = dataContext;

			Initialize();

			_administrationBar = new AdministrationBar(this, _showAdminBar);
			_urlHelper = new Lazy<UrlHelper>(() => new UrlHelper(_httpContext.Request.RequestContext));
		}

		public HttpContextBase HttpContext
		{
			get
			{
				return _httpContext;
			}
		}

		public DataContext Data
		{
			get
			{
				return _dataContext;
			}
		}

		public SlamCmsConfiguration Configuration
		{
			get
			{
				return _configuration;
			}
		}

		public ILogManager LogManager
		{
			get
			{
				return _configuration.Services.GetService<ILogManager>();
			}
		}

		public IProfiler Profiler
		{
			get
			{
				return _configuration.Services.GetService<IProfiler>();
			}
		}

		public ICookieManager CookieManager
		{
			get
			{
				return _cookieManager;
			}
		}

		public UrlHelper Url
		{
			get
			{
				return _urlHelper.Value;
			}
		}

		public AdministrationBar AdministrationBar
		{
			get
			{
				return _administrationBar;
			}
		}

		public User User { get; private set; }

		public bool PreviewMode { get; private set; }

		/// <summary>
		/// Creates a new SlamCms content query builder
		/// </summary>
		/// <param name="options">Options for the default QueryBuilder behavior</param>
		/// <returns></returns>
		public QueryBuilder CreateQuery(QueryBuilderOptions options)
		{
			return CreateQuery(new string[] {});
		}

		/// <summary>
		/// Creates a new SlamCms content query builder
		/// </summary>
		/// <param name="zoneId">The ZoneId of the zone you want content that belongs to</param>
		/// <returns></returns>
		public QueryBuilder CreateQuery(string zoneId)
		{
			return CreateQuery(new[] { zoneId });
		}

		/// <summary>
		/// Creates a new SlamCms content query builder
		/// </summary>
		/// <param name="zoneIds">The ZoneId of the zones you want content that belongs to</param>
		/// <returns></returns>
		public QueryBuilder CreateQuery(string[] zoneIds)
		{
			var query = _dataContext.CreateQuery();

			if (PreviewMode)
			{
				query.FilterStatus(FilterStatus.PreviewMode);
				query.Cache(QueryCacheBehavior.NoCache);
			}

			if (zoneIds != null && zoneIds.Any())
				query.FilterZone(zoneIds);

			return query;
		}

		/// <summary>
		/// Retrieves a single content based on the PrimaryContentItemId
		/// </summary>
		/// <param name="primaryContentItemId">The PrimaryContentItemId (SlamId) of the content</param>
		/// <returns></returns>
		public ContentItem GetContent(string primaryContentItemId)
		{
			var query = _dataContext.CreateQuery()
									.FilterContentItemId(primaryContentItemId);

			if (User.IsAdministrator() && query.Parameters.Status == FilterStatus.LiveAndNotExpired)
				query.FilterStatus(FilterStatus.Live);

			return query.GetFirstOrDefault();
		}

		/// <summary>
		/// Retrieves a single content based on the PrimaryContentItemId
		/// </summary>
		/// <param name="primaryContentItemId">The PrimaryContentItemId (SlamId) of the content</param>
		/// <returns></returns>
		public T GetContent<T>(string primaryContentItemId) where T : ContentItem, new()
		{
			var query = _dataContext.CreateQuery()
									.FilterContentItemId(primaryContentItemId);

			// administrators should be able to retrieve expired content
			if (User.IsAdministrator() && query.Parameters.Status == FilterStatus.LiveAndNotExpired)
				query.FilterStatus(FilterStatus.Live);

			return query.GetFirstOrDefault<T>();
		}

		/// <summary>
		/// Retrieves the TagTree corresponding to the ZoneId passed as parameter. If no ZoneId then it will retrieve the default TagTree.
		/// </summary>
		/// <param name="zoneId">The ZoneId to get the TagTree</param>
		/// <returns></returns>
		public TagTree GetTagTree(string zoneId = null)
		{
			return _dataContext.GetTagTree(zoneId);
		}

		protected void Initialize()
		{
			using (Profiler.Step("[SlamCMS] Context Initialization"))
			{
				User = DependencyResolver.Current.GetService<ISlamCmsUserResolver>().GetUser(this);

				// check user not authenticated
				if (User == null)
					return;

				// restore AdminBar cookie if not present and is administrator
				if (User.IsAdministrator() && !_cookieManager.Contains(Constants.Cookies.AdminBar))
					_cookieManager.Set(Constants.Cookies.AdminBar);

				// show admin bar based on cookie value
				_showAdminBar = User.IsAdministrator() && (_cookieManager.Get(Constants.Cookies.AdminBar) ?? "0") == "1";

				// preview mode based on cookie value
				PreviewMode = User.IsAdministrator() && (_cookieManager.Get(Constants.Cookies.PreviewMode) ?? "0") == "1";
			}
		}
	}
}